#!/usr/bin/env python
#
# Find big messages in my GMail Box
# Author:  Yotam Medini  yotam.medini@gmail.com -- Created: 2011/August/18

import email
import email.header
import getpass
import imaplib
import optparse
import string
import sys
import time


RFC822_HEADER = "RFC822.HEADER"
RFC822_SIZE = "RFC822.SIZE"


def safeint(s, ret=None):
    try:
        ret = int(s)
    except:
        pass
    return ret


class EmailInfo:
    
    def __init__(self):
        self.From = None
        self.Date = None
        self.Subject = None
        self.size = None

    def __str__(self):
        return "From: %s, Date: %s, Subject: %s, size= %d" % (
            self.From, self.Date, self.Subject, self.size)


def strnow():
    "Return current time as yyyy/mm/dd:HH:MM:SS"
    now = int(time.time())
    nowlocal = time.localtime(now)
    lta = time.localtime(now)
    s = "%d/%02d/%02d:%02d:%02d:%02d" % lta[0:6]
    return s


class GMailStart:


    def __init__(self, argv):
        self.rc = 0
        self.argv = argv
        self.parse_args()
           

    def parse_args(self):
        usage = "usage: %prog [options]"
        parser = optparse.OptionParser(usage=usage)
        parser.add_option("-u", "--uname", dest="uname",
                          help="GMail user name")
        parser.add_option("", "--pw", dest="pw",
                          help="GMail password [may be prompted]")
        parser.add_option("", "--minsize", dest="minsize", type="int",
                          default="1000000",
                          help="Minimal size for item [%default]")
        parser.add_option("", "--skip", dest="skip", type="int",
                          default="0",
                          help="Number of UIDs to skip [%default]")
        parser.add_option("", "--batch", dest="batch", type="int",
                          default="0x10",
                          help="Fetch batch size [%default]")
        parser.add_option("-n", "--nresults", dest="nresults", type="int",
                          default="10",
                          help="maximal number of results")
        parser.add_option("", "--debug", dest="debug", type="int", default=1,
                          help="Debug flags [%default]");
        (self.options, self.initial_keys) = parser.parse_args(self.argv)
        if self.options.uname is None:
            parser.error("Missing uname")
            self.rc = 1


    def ok(self):
        return self.rc == 0


    def run(self):
        self.login2gmail()
        boxname = '[Gmail]/All Mail'
        self.log("select '%s'" % boxname)
        n = self.gmail.select(boxname, readonly=True)
        self.log("Box(%s): %s" % (boxname, str(n)))
        minsize = self.options.minsize
        self.log("search for mail items larger than %d bytes" % minsize)
        typ, data = self.gmail.search(None, '(LARGER %d)' % minsize)
        self.log("Found items, len(data)=%d" % len(data[0]))
        self.uids_emailinfos = []
        self.get_email_info(data[0])
        self.log("GMail logout")
        self.gmail.logout();
        self.uids_emailinfos.sort(lambda x, y: x[1].size - y[1].size)
        for uei in self.uids_emailinfos:
            sys.stdout.write("[%5d] %s\n" % (uei[0], uei[1]))

       

    def login2gmail(self):
        name_gmail = "%s@gmail.com" % (self.options.uname)
        pw = getpass.getpass() if self.options.pw is None else self.options.pw 
        self.log("Connecting to GMail")
        self.gmail = imaplib.IMAP4_SSL('imap.gmail.com', 993)
        self.log("GMail login")
        self.gmail.login(name_gmail, pw)
        

    def get_email_info(self, data0):
        ids = data0.split()
        n_ids_all = len(ids)
        ids = ids[self.options.skip:]
        n_ids = len(ids)
        n = min(self.options.nresults, n_ids)
        self.log("Fetching %d items out of %d ids" % (n, n_ids))
        i = 0
        batch = self.options.batch
        for i in range(0, n, batch):
            ids_batch = ids[i: i + batch]
            self.log("Fetching batch [%d, %d]" % (i, min(i + batch, n)))
            self.uids_emailinfos += self.fetch_batch(ids[i: i + batch])
            

    def fetch_batch(self, ids):
        ids_comma = ','.join(ids)
        self.log(RFC822_HEADER)
        typ, datah = self.gmail.fetch(ids_comma, '(%s)' % RFC822_HEADER)
        self.log(RFC822_SIZE)
        typ, datasz = self.gmail.fetch(ids_comma, '(UID %s)' % RFC822_SIZE)
        self.log("|datah|=%d, |datasz|=%d\n" % (len(datah), len(datasz)))
        uid2ei = {}
        for item in datah:
            if isinstance(item, tuple) and len(item) == 2:
                uid = safeint(string.split(item[0])[0])
                if not uid is None:
                    ei = EmailInfo()
                    raw_msg = item[1]
                    msg = email.message_from_string(raw_msg)
                    ei.From = msg['from']
                    ei.Date = msg['date']
                    ei.Subject = msg['subject']
                    uid2ei[uid] = ei
        h_keys = uid2ei.keys()[:]
        for item in datasz:
            uid = safeint(string.split(item)[0])
            if uid in h_keys:
                tail = string.split(item, RFC822_SIZE)[-1]
                size = safeint(string.split(tail, ')')[0])
                if not size is None:
                    uid2ei[uid].size = size
        ueis = filter(lambda kv: not kv[1].size is None, uid2ei.items())
        return ueis




    def log(self, msg):
        sys.stderr.write("%s %s\n" % (strnow(), msg))
                         
        
if __name__ == "__main__":
    p = GMailStart(sys.argv)
    if p.ok():
        p.run()
    sys.exit(p.rc)
